package croppingplanap;

import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CroppingPlan {
	

	private Map<Integer, Category> categories;
	private Map<Integer, Crop> crops;
	
	private Crop[][] plan;
	private int width;
	private int height;
	
	public CroppingPlan(int width, int height){
		this.width = width;
		this.height = height;
		
		plan = new Crop[width][height];
		for(int x = 0; x < width; x++){
			for(int y = 0; y < height; y++){
				plan[x][y] = Crop.NOTHING;
			}
		}
		
		//categories = Collections.unmodifiableMap(generateCategories());
		//crops = Collections.unmodifiableList(generateCrops(categories));
				
	}
	
	private CroppingPlan(){
		
	}
	
	public void setRect(Point p1, Point p2, Crop newCrop){
		Point p = new Point();
		int xMin = Math.max(0, Math.min(p1.x, p2.x));
		int yMin = Math.max(0, Math.min(p1.y, p2.y));
		int xMax = Math.min(width - 1, Math.max(p1.x, p2.x));
		int yMax = Math.min(height - 1, Math.max(p1.y, p2.y));
		
		for(p.x = xMin; p.x <= xMax; p.x++){
			for(p.y = yMin; p.y <= yMax; p.y++){
				if(plan[p.x][p.y] != null)plan[p.x][p.y] = newCrop;
			}
		}
	}
	
	public Crop getCropAt(Point p){
		return (p.x < 0 || p.x >= width || p.y < 0 || p.y >= height) ? null : plan[p.x][p.y];
	}
	
	public Crop getCropAt(int x, int y){
		return (x < 0 || x >= width || y < 0 || y >= height) ? null : plan[x][y];
	}
	
	public Map<Integer, Category> getCategories(){
		return categories;
	}
	
	public Map<Integer, Crop> getCrops(){
		return crops;
	}
	
	
	
	private static Map<Integer, Category> generateCategories(){
		
		return parseCategories("category1;1!category2;2!category3;3!");
		/*Map<Integer, Category> result = new HashMap<Integer, Category>();
		
		result.put(1, new Category("Amaranthaceae", 1));
		
		return result;*/
	}
	
	private static Map<Integer, Crop> generateCrops(Map<Integer, Category> categories){
		/*Map<Integer, Crop> result = new HashMap<Integer, Crop>();
		ColorModel model = new ColorModel();
		Category category = categories.get(0);
		
		String[] names = {"Beet", "Spinach", "Swiss Chard"};
		for(int i = 0; i < 3; i++){
			result.put(i, new Crop(names[i], category, i, Crop.getRNDColor()));	
		}
		
		return result;*/
		return parseCrops("cropname1;1;1!cropname2;2;2!cropname3;3;3!carrot;4;1!onion;5;2!potato;6;3!cucumber;7;1!lettuce;8;2!kumquat;9;1!red pepper;10;2!", categories);
		
	}

	private static Crop generateCrop(String name, String catName, Map<String, Category> categories, ColorModel model){
		Category cat = categories.get(catName);
		Color c = model.getNext(cat.getCatColor());
		return null;//new Crop(name, cat, c);
	}

	public String serialize(){
		StringBuilder sb = new StringBuilder();
		sb.append(width);
		sb.append(';');
		sb.append(height);
		sb.append(';');
		for(int y = 0; y < height; y++){
			for(int x = 0; x < width; x++){
				if(plan[x][y] == null){
					sb.append("**");
				}else if(plan[x][y] == Crop.NOTHING){
					sb.append("__");
				}else{
					if(plan[x][y].getId() < 10) sb.append('0');
					sb.append(plan[x][y].getId());
				}
			}
		}
		return sb.toString();
	}
	
	public static CroppingPlan unserialize(String mapStr){
		Map<Integer, Category> categories = Collections.unmodifiableMap(generateCategories());
		Map<Integer, Crop> crops = Collections.unmodifiableMap(generateCrops(categories));
		return unserialize(categories, crops, mapStr);
	}
	
	private static CroppingPlan unserialize(Map<Integer, Category> categories, Map<Integer, Crop> crops, String mapStr){
		CroppingPlan result = new CroppingPlan();
		Crop crop;
		result.categories = categories;
		result.crops = crops;
		
		int lastIndex = 0;
		int nextIndex = 0;
		
		nextIndex = mapStr.indexOf(';');
		if(nextIndex < 0) throw new RuntimeException("No semicolons. Width is supposed to be specifed, followed by a semicolon");
		try{
			result.width = Integer.parseInt(mapStr.substring(0,nextIndex));
		}catch(NumberFormatException e){
			throw new RuntimeException("Invalid format \"" + mapStr.substring(0,nextIndex) +"\" for width");
		}
		if(result.width <= 0) throw new RuntimeException("Non-positive width");
		
		lastIndex = nextIndex;
		nextIndex = mapStr.indexOf(';', lastIndex+1);
		if(nextIndex < 0) throw new RuntimeException("Expected semicolon. Height is supposed to be specifed after width is, followed by a semicolon");
		try{
			result.height = Integer.parseInt(mapStr.substring(lastIndex+1,nextIndex));
		}catch(NumberFormatException e){
			throw new RuntimeException("Invalid format \"" + mapStr.substring(lastIndex+1,nextIndex) +"\" for height");
		}
		if(result.height <= 0) throw new RuntimeException("Non-positive height");
		
		result.plan = new Crop[result.width][result.height];
		
		String cropName;
		int curId;
		nextIndex++;
		for(int y = 0; y < result.height; y++){
			for(int x = 0; x < result.width; x++){
				if(mapStr.length() < nextIndex + 2){
					throw new RuntimeException("Map string shorter than expected");
				}
				cropName = mapStr.substring(nextIndex, nextIndex + 2);
				if(cropName.equals("**")){
					result.plan[x][y] = null;
				}
				else if(cropName.equals("__")) result.plan[x][y] = Crop.NOTHING;
				else{
					try{
						curId = Integer.parseInt(cropName);
					}catch(NumberFormatException e){
						throw new RuntimeException(String.format("Invalid crop id \"%s\" at index %d", cropName, nextIndex));
					}
					crop = result.crops.get(curId);
					if(crop == null){
						throw new RuntimeException(String.format("Unrecognized crop id \"%s\" at index %d", cropName, nextIndex));
					}
					result.plan[x][y] = crop;
				}
				nextIndex += 2;
			}
		}
		
		return result;

	}
	
	public static CroppingPlan unserialize(String catStr, String cropStr, String mapStr){
		Map<Integer, Category> categories = parseCategories(catStr); 
		return unserialize(categories, parseCrops(cropStr, categories), mapStr);
	}
	
	public static Map<Integer, Crop> parseCrops(String cropStr, Map<Integer, Category> categories){
		Map<Integer, Crop> result = new HashMap<Integer, Crop>();
		String name;
		String idStr;
		int id;
		String catIdStr;
		int catId;
		Category category;
		int lastIndex = -1;
		int nextIndex = cropStr.indexOf(';');
		while(nextIndex != -1){
			name = cropStr.substring(lastIndex+1, nextIndex);
			lastIndex = nextIndex;
			nextIndex = cropStr.indexOf(';', lastIndex+1);
			if(nextIndex == -1) throw new RuntimeException("Expected \'!\' in crop string");
			idStr = cropStr.substring(lastIndex+1, nextIndex);
			try{
				id = Integer.parseInt(idStr);
			}catch(NumberFormatException e){
				throw new RuntimeException("Invalid id \"" + idStr + "\" in crop string");
			}
			lastIndex = nextIndex;
			nextIndex = cropStr.indexOf('!', lastIndex+1);
			if(nextIndex == -1) throw new RuntimeException("Expected \'!\' in crop string");
			catIdStr = cropStr.substring(lastIndex+1, nextIndex);
			if(catIdStr.length() == 0){
				category = Category.NOTHING;
			}else{
				try{
					catId = Integer.parseInt(catIdStr);
				}catch(NumberFormatException e){
					throw new RuntimeException("Invalid category id \"" + catIdStr + "\" in crop string");
				}
				
				category = categories.get(catId);
				if(category == null) throw new RuntimeException("Unknown category id " + catId);
			}
			result.put(id, new Crop(name, category, id, Crop.getRNDColor()));
			lastIndex = nextIndex;
			nextIndex = cropStr.indexOf(';', lastIndex+1);
		}
		return result;
		
	}
	
	public static Map<Integer, Category> parseCategories(String catStr){
		Map<Integer, Category> result = new HashMap<Integer, Category>();
		String name;
		String idStr;
		int id;
		int lastIndex = -1;
		int nextIndex = catStr.indexOf(';');
		while(nextIndex != -1){
			name = catStr.substring(lastIndex+1, nextIndex);
			lastIndex = nextIndex;
			nextIndex = catStr.indexOf('!', lastIndex+1);
			if(nextIndex == -1) throw new RuntimeException("Expected \'!\' in category string");
			idStr = catStr.substring(lastIndex+1, nextIndex);
			try{
				id = Integer.parseInt(idStr);
			}catch(NumberFormatException e){
				throw new RuntimeException("Invalid id \"" + idStr + "\" in category string");
			}
			result.put(id, new Category(name, id));
			lastIndex = nextIndex;
			nextIndex = catStr.indexOf(';', lastIndex+1);
		}
		return result;
	}
	
	public int getWidth(){
		return width;
	}
	public int getHeight(){
		return height;
	}
	
}
